package la.renzhen.rtpt.admin.controller;

import la.renzhen.rtpt.admin.boot.starter.CenterServerProperties;
import la.renzhen.rtpt.admin.source.CenterConfigurationSource;
import la.renzhen.rtpt.admin.source.ConfigElement;
import la.renzhen.rtpt.admin.util.Strings;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <p>
 *
 * @author <a href="mailto:zhouhaichao@2008.sina.com">haiker</a>
 * @version 14/05/2018 8:31 PM
 */
@Slf4j
@Controller
@RequestMapping(value = "${rtpt.admin.context-path:/rtpt}/ui")
public class AdminUIController {

    @Value("${rtpt.admin.context-path:/rtpt}")
    String contextPath;

    @Autowired
    CenterConfigurationSource configurationSource;

    @Autowired
    CenterServerProperties centerServerProperties;

    @Autowired(required = false)
    DiscoveryClient discoveryClient;

    List<ConfigElement> changedKeys = new ArrayList<ConfigElement>();

    RestTemplate restTemplate = new RestTemplate();

    protected void populateBase(HttpServletRequest request, Map<String, Object> model) {
        model.put("basePath", contextPath);
        model.put("activeEnv", Strings.empty(request.getParameter("env"), centerServerProperties.getDefaultEnvironment()));
        model.put("prefix", Strings.empty(request.getParameter("prefix")));
    }

    @RequestMapping(path = {"", "/"}, method = {RequestMethod.GET, RequestMethod.POST})
    public String home(HttpServletRequest request, Map<String, Object> model) {
        populateBase(request, model);

        String prefix = model.get("prefix").toString();
        String env = model.get("activeEnv").toString();

        model.put("editable", true);
        model.put("envs", configurationSource.environments());
        model.put("items", configurationSource.loadAll(env, prefix));
        model.put("notifyClient", !changedKeys.isEmpty());

        return "rtpt/index";
    }

    @PostMapping("/add")
    public String add(
            @RequestParam("itemKey") String key,
            @RequestParam("itemValue") String value,
            @RequestParam(value = "itemEnv", required = false) String env,
            @RequestParam(value = "itemInfo", required = false) String info) {
        String itemEnv = Strings.empty(env, centerServerProperties.getDefaultEnvironment());
        ConfigElement element = new ConfigElement(key, value, itemEnv, new Date(), info);
        configurationSource.set(element);
        changedKeys.add(element);
        return "redirect:" + contextPath + "/ui?env=" + itemEnv;
    }


    @GetMapping("/del")
    public String del(
            @RequestParam("itemKey") String key, @RequestParam("itemEnv") String env
    ) {
        changedKeys.add(new ConfigElement(key, "", env, null, ""));
        configurationSource.del(env, key);
        return "redirect:" + contextPath + "/ui?env=" + env;
    }

    /**
     * 通知客户端
     *
     * @return 返回地址
     */
    @GetMapping("/notify")
    public String notifyClient() {
        if (discoveryClient != null) {
            log.info("管理段使用注册服务，通知客户端");
            this.notifyAllCenterClient();
        }
        changedKeys.clear();
        return "redirect:" + contextPath + "/ui";
    }

    @SneakyThrows
    protected void notifyAllCenterClient() {
        List<ServiceInstance> serviceInstances = new ArrayList<>();
        for (String serviceId : discoveryClient.getServices()) {
            List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
            for (ServiceInstance instance : instances) {
                if (instance.getMetadata().containsKey("X-RTPT-CONFIG-TOKEN")) {
                    log.debug("服务{}，{}使用配置中心", serviceId, instance.getUri().toString());
                    serviceInstances.add(instance);
                }else{
                    log.debug("服务{}，{}未使用动态配置中心", serviceId, instance.getUri().toString());
                }
            }
        }
        if (serviceInstances.isEmpty()) {
            return;
        }
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        CountDownLatch latch = new CountDownLatch(serviceInstances.size());
        for (ServiceInstance serviceInstance : serviceInstances) {
            executorService.submit(() -> {
                notifyService(serviceInstance);
                latch.countDown();
            });
        }
        latch.await();
        executorService.shutdown();
    }

    @Retryable
    protected void notifyService(ServiceInstance serviceInstance) {
        log.info("通知服务{},{}刷新服务",serviceInstance.getServiceId(),serviceInstance.getUri().toString());
        HttpStatus status = restTemplate.execute(serviceInstance.getUri(), HttpMethod.GET, r -> {
        }, ClientHttpResponse::getStatusCode);

        assert status.is2xxSuccessful();
    }
}
